package com.lagou.edu.proxy;

import com.czp.annotation.MyTransactional;
import com.lagou.edu.utils.TransactionManager;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ProxyImpl implements InvocationHandler, MethodInterceptor {

    private TransactionManager transactionManager;
    private Object obj ;
    public ProxyImpl(Object obj,TransactionManager transactionManager){
        this.obj = obj;
        this.transactionManager = transactionManager;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return doInvoke(method,args);
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        return doInvoke(method,objects);
    }


    private Object doInvoke(Method method, Object[] args) throws Exception {
        Object result = null;

        Method declaredMethod = obj.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
        MyTransactional myTransactional = declaredMethod.getAnnotation(MyTransactional.class);
        /**
         * 没有声明事务注解, 直接执行
         */
        if(myTransactional == null){
            return method.invoke(obj,args);
        }
        try{
            // 开启事务(关闭事务的自动提交)
            transactionManager.beginTransaction();
            result = method.invoke(obj,args);
            // 提交事务
            transactionManager.commit();
        }catch (Exception e) {
            e.printStackTrace();
            /**
             * 异常符合自定义 需要回滚的异常才执行回滚操作
             */

            Class<? extends Throwable>[] classes = myTransactional.rollbackFor();
            for (Class<? extends Throwable> aClass : classes) {
                if (aClass.isAssignableFrom(e.getClass())) {
                    // 回滚事务
                    transactionManager.rollback();
                    // 抛出异常便于上层servlet捕获
                    throw e;
                }
            }
        }

        return result;
    }
}
